package com.poly.flowable.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.poly.common.core.constant.Constants;
import com.poly.common.core.tools.utils.Func;
import com.poly.common.core.utils.AssertUtil;
import com.poly.common.core.utils.ConvertUtils;
import com.poly.common.core.utils.DateUtils;
import com.poly.common.core.utils.IdUtil;
import com.poly.flowable.cache.FormCache;
import com.poly.flowable.domain.SysDeployForm;
import com.poly.flowable.domain.SysForm;
import com.poly.flowable.enums.form.DataTypeEnum;
import com.poly.flowable.enums.form.SceneGroupEnum;
import com.poly.flowable.enums.form.SystemFieldEnum;
import com.poly.flowable.mapper.SysFormMapper;
import com.poly.flowable.pojo.dto.FormFieldDTO;
import com.poly.flowable.pojo.dto.FormItemDTO;
import com.poly.flowable.service.ISysDeployFormService;
import com.poly.flowable.service.ISysFormService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 流程表单Service业务层处理
 *
 * @author Yang huijing
 * @date 2021-04-03
 */
@Service
public class SysFormServiceImpl implements ISysFormService {
    @Autowired
    private SysFormMapper sysFormMapper;
    @Resource
    private DataSource dataSource;

    @Autowired
    private ISysDeployFormService deployFormService;

    /**
     * 查询流程表单
     *
     * @param formId 流程表单ID
     * @return 流程表单
     */
    @Override
    public SysForm selectSysFormById(Long formId) {
        return sysFormMapper.selectSysFormById(formId);
    }

    /**
     * 查询流程表单列表
     *
     * @param sysForm 流程表单
     * @return 流程表单
     */
    @Override
    public List<SysForm> selectSysFormList(SysForm sysForm) {
        return sysFormMapper.selectSysFormList(sysForm);
    }

    /**
     * 新增流程表单
     *
     * @param sysForm 流程表单
     * @return 结果
     */
    @Override
    public int insertSysForm(SysForm sysForm) {
        sysForm.setCreateTime(DateUtils.getNowDate());
        return sysFormMapper.insertSysForm(sysForm);
    }

    /**
     * 修改流程表单
     *
     * @param sysForm 流程表单
     * @return 结果
     */
    @Override
    public int updateSysForm(SysForm sysForm) {
        sysForm.setUpdateTime(DateUtils.getNowDate());
        return sysFormMapper.updateSysForm(sysForm);
    }

    /**
     * 批量删除流程表单
     *
     * @param formIds 需要删除的流程表单ID
     * @return 结果
     */
    @Override
    public int deleteSysFormByIds(Long[] formIds) {
        return sysFormMapper.deleteSysFormByIds(formIds);
    }

    /**
     * 删除流程表单信息
     *
     * @param formId 流程表单ID
     * @return 结果
     */
    @Override
    public int deleteSysFormById(Long formId) {
        return sysFormMapper.deleteSysFormById(formId);
    }

    /**
     * 检查表格数据是否存在
     *
     * @param formName --表格名称
     * @return
     */
    public boolean existByTableName(String formName) {
        return sysFormMapper.existByTableName(formName) > 0;
    }

    /**
     * 真实表是否存在
     * @param tableName
     * @return
     */
    public boolean existRealTable(String tableName) {
        try {
            Connection connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
            return rs.getRow() > 0;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 获取真实表的所有字段
     * @param tableName
     * @return
     */
    public List<String> getRealTableField(String tableName) {
        List<String> list = new ArrayList<>();
        try {
            Connection connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getColumns(null, null, tableName, "%");
            while (rs.next()) {
                list.add(rs.getString("COLUMN_NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 新增流程表单
     *
     * @param sysForm 流程表单
     * @return 结果
     */
    @Override
    @Transactional
    public int insertForm(SysForm sysForm) {
        AssertUtil.isNotNull(sysForm, "参数错误");
        AssertUtil.isNotBlank(sysForm.getFormName(), "表单名称不允许为空！");
        String tableName = Constants.FORM_PREFIX.concat(sysForm.getFormName());
        AssertUtil.isTrue(tableName.matches(Constants.FORM_NAME_FORMAT), "表单名称只能由字母下滑线数字组成");
        AssertUtil.isFalse(existByTableName(sysForm.getFormName()) || existRealTable(tableName), "表名已经存在！");
        sysForm.setCreateTime(DateUtils.getNowDate());
//        if (Func.isEmpty(sysForm.getTableType())) {
//            sysForm.setTableType(0);
//        }
        JSONObject content = JSON.parseObject(sysForm.getFormContent());
        List<FormFieldDTO> formFields = getFormField(content);
        if (Func.isNotEmpty(formFields)) {
            Map<String, Long> groupFn = formFields.stream().collect(Collectors.groupingBy(FormFieldDTO::getFieldName, Collectors.counting()));
            for (String k : groupFn.keySet()) {
                AssertUtil.isFalse(groupFn.get(k) > 1, "[" + k + "]字段名称已重复！");
            }
        }
        //生成key
        sysForm.setFormContent(generateKey(content));
        int count = sysFormMapper.insertSysForm(sysForm);
        if (count > 0) {
            //过滤系统默认字段
            List<String> sysFieldList = new ArrayList<>(SystemFieldEnum.values().length);
            for (SystemFieldEnum sfe : SystemFieldEnum.values()) {
                sysFieldList.add(sfe.getFieldName());
            }
            List<FormFieldDTO> createFields = formFields.stream().filter(p -> !sysFieldList.contains(p.getFieldName()) && Func.isEmpty(p.getListener())).collect(Collectors.toList());
            sysFormMapper.createTable(0, tableName, sysForm.getRemark(), createFields);
        } else {
            throw new IllegalArgumentException("保存失败！");
        }
        FormCache.clear();
        return count;
    }

    @Override
    @Transactional
    public int updateForm(SysForm sysForm) {
        AssertUtil.isNotNull(sysForm, "参数错误");
        AssertUtil.isNotBlank(sysForm.getFormName(), "表单名称不允许为空！");
        String tableName = Constants.FORM_PREFIX.concat(sysForm.getFormName());
        AssertUtil.isTrue(tableName.matches(Constants.FORM_NAME_FORMAT), "表单名称只能由字母下滑线数字组成");
        SysForm form = sysFormMapper.selectSysFormById(sysForm.getFormId());
        AssertUtil.isNotNull(form, "表单不存在");
        String srcTableName = Constants.FORM_PREFIX.concat(form.getFormName());
        JSONObject content = JSON.parseObject(sysForm.getFormContent());
        List<FormFieldDTO> newFormFields = getFormField(content);
        List<FormFieldDTO> ufa = new ArrayList<>();
        if (Func.isNotEmpty(newFormFields)) {
            Map<String, Long> groupFn = newFormFields.stream().collect(Collectors.groupingBy(FormFieldDTO::getFieldName, Collectors.counting()));
            for (String k : groupFn.keySet()) {
                AssertUtil.isFalse(groupFn.get(k) > 1, "[" + k + "]字段名称已重复！");
            }
//            Map<String, List<FormFieldDTO>> groupK = newFormFields.stream().collect(Collectors.groupingBy(FormFieldDTO::getKey));
//            for (String k : groupK.keySet()) {
//                if (groupK.get(k).size() > 1) {
//                    AssertUtil.isFalse(true, "[" + groupK.get(k).get(0).getFieldName() + "]字段的Key已重复！");
//                }
//            }
            if (!form.getFormName().equals(sysForm.getFormName())) {
                AssertUtil.isFalse(existByTableName(sysForm.getFormName()) || existRealTable(tableName), "表名已经存在！");
            }
            List<FormFieldDTO> oldFormFields = getFormField(form.getFormContent());
            List<String>  tableFieldList = getRealTableField(srcTableName);
            List<String> newKeyList = new ArrayList<>();
            for (FormFieldDTO fa : newFormFields) {
                FormFieldDTO oldFormField = oldFormFields.stream().filter(p -> p.getKey().equals(fa.getKey())).findFirst().orElse(null);
                if (Func.isEmpty(oldFormField) || !tableFieldList.contains(fa.getFieldName())) {
                    newKeyList.add(fa.getKey());
                    //如果 在老的字段数据中不存在或在实际表中字段不存在时 添加字段
                    ufa.add(fa);
                } else if (!fa.getFieldName().equals(oldFormField.getFieldName())
                        || !fa.getDataType().equals(oldFormField.getDataType())
                        || (Func.isNotEmpty(fa.getDefaultValue()) && !fa.getDefaultValue().equals(oldFormField.getDefaultValue()))
                        || !fa.getRequired().equals(oldFormField.getRequired())
                        || !fa.getComment().equals(oldFormField.getComment())
                ) {
                    //如果 字段名称，数据类型，默认值，是否必填，注释等其中有不相同时 更新字段
                    fa.setOldFieldName(oldFormField.getFieldName());
                    ufa.add(fa);
                }
            }
            if (Func.isNotEmpty(newKeyList)) {
                sysForm.setFormContent(generateKey(content, newKeyList));
            }
        }

        sysForm.setUpdateTime(new Date());
        int count = sysFormMapper.updateSysForm(sysForm);
        AssertUtil.isTrue(count > 0, "保存失败！");
        if (Func.isNotEmpty(ufa)) {
            System.out.println(JSON.toJSONString(ufa));
            //过滤系统默认字段
            List<String> sysFieldList = new ArrayList<>(SystemFieldEnum.values().length);
            for (SystemFieldEnum sfe : SystemFieldEnum.values()) {
                sysFieldList.add(sfe.getFieldName());
            }
            List<FormFieldDTO> updateFields = ufa.stream().filter(p -> !sysFieldList.contains(p.getFieldName()) && Func.isEmpty(p.getListener())).collect(Collectors.toList());
            sysFormMapper.updateTableField(srcTableName, updateFields);
        }
        if (!form.getFormName().equals(sysForm.getFormName())) {
            //更新表名
            sysFormMapper.updateTableName(srcTableName, tableName, sysForm.getRemark());
        }
        FormCache.clear();
        return count;
    }


    /**
     * 生成KEY
     *
     * @param model
     * @param keyList
     * @return
     */
    public String generateKey(JSONObject model, List<String> keyList) {
        if (Func.isNotEmpty(model)) {
            JSONArray fields = model.getJSONArray("widgetList");
            for (int i = 0; i < fields.size(); i++) {
                JSONObject o = fields.getJSONObject(i);
                if (Func.isEmpty(keyList) || keyList.contains(o.getString("key"))) {
                    o.put("key", IdUtil.getIdToString());
                }
            }
        }
        return JSON.toJSONString(model);
    }

    /**
     * 生成KEY
     *
     * @param model
     * @param keys
     * @return
     */
    public String generateKey(JSONObject model, String... keys) {
        return generateKey(model, ListUtil.toList(keys));
    }

    @Override
    public List<FormFieldDTO> getFormField(JSONObject model) {
        List<FormFieldDTO> formFields = null;
        if (Func.isNotEmpty(model)) {
            JSONArray fields = model.getJSONArray("widgetList");
            formFields = new ArrayList<>();
            for (int i = 0; i < fields.size(); i++) {
                JSONObject field = fields.getJSONObject(i);
                FormFieldDTO fa = new FormFieldDTO();
                fa.setKey(field.getString("key"));
                fa.setControlType(field.getString("type"));
                JSONObject option = field.getJSONObject("options");
                fa.setFieldName(option.getString("name"));
                fa.setComment(option.getString("label"));
                fa.setQueryType(option.getString("queryType"));
                if (option.getJSONArray("sceneGroup") != null) {
                    JSONArray arr = option.getJSONArray("sceneGroup");
                    fa.setSceneGroup(arr.toJavaList(String.class));
                }
                fa.setDictName(option.getString("dictName"));
                fa.setListener(option.getString("listener"));
                fa.setListenerFieldName(option.getString("listenerFieldName"));
                fa.setRequired(option.getBoolean("required"));
                fa.setMultiple(option.getBoolean("multiple"));
                fa.setMultipleLimit(option.getBoolean("multipleLimit"));
                fa.setMinLength(option.getLong("minLength"));
                fa.setMaxLength(option.getLong("maxLength"));
                fa.setDefaultValue(option.get("defaultValue"));
                JSONArray oi = option.getJSONArray("optionItems");
                if (Func.isNotEmpty(oi)) {
                    List<FormItemDTO> itemList = new ArrayList<>();
                    for (int k = 0; k < oi.size(); k++) {
                        JSONObject o = oi.getJSONObject(k);
                        itemList.add(new FormItemDTO(o.getString("label"), o.getString("value")));
                    }
                    fa.setItemList(itemList);
                }
                if (option.get("defaultValue") instanceof JSONArray) {
                    JSONArray arr = option.getJSONArray("defaultValue");
                    List<String> ls = arr.toJavaList(String.class);
                    fa.setDefaultValue(Func.join(ls));
                } else {
                    fa.setDefaultValue(option.get("defaultValue"));
                }
                if (option.containsKey("precision")) {
                    fa.setPrecision(option.getInteger("precision"));
                }
                if ("number".equals(fa.getControlType())) {
                    if (Func.isNotEmpty(fa.getPrecision()) && fa.getPrecision() > 0) {
                        fa.setDataType(DataTypeEnum.DECIMAL.name());
                        if (Func.isNotEmpty(fa.getMaxLength())) {
                            fa.setLen(fa.getMaxLength().toString().length());
                        } else {
                            fa.setLen(20);
                        }
                    } else if ((Func.isNotEmpty(fa.getMaxLength()) && fa.getMaxLength() > 2147483647L)
                            || (Func.isNotEmpty(fa.getMinLength()) && fa.getMinLength() < -2147483648L)) {
                        fa.setDataType(DataTypeEnum.BIGINT.name());
                    } else {
                        fa.setDataType(DataTypeEnum.INT.name());
                    }
                } else if ("date".equals(fa.getControlType())) {
                    fa.setDataType(DataTypeEnum.DATETIME.name());
//                    fa.setDataType(FormDataTypeEnum.DATE.name());
                } else if ("switch".equals(fa.getControlType())) {
                    fa.setDataType(DataTypeEnum.BIT.name());
                } else if ("rich-editor".equals(fa.getControlType()) || (Func.isNotEmpty(fa.getMaxLength()) && fa.getMaxLength() > 2000)) {
                    fa.setDataType(DataTypeEnum.TEXT.name());
                } else {
                    fa.setDataType(DataTypeEnum.VARCHAR.name());
                    fa.setLen(ConvertUtils.toInteger(fa.getMaxLength(), 255));
                }
                formFields.add(fa);
            }
        }
        return formFields;
    }

    @Override
    public List<FormFieldDTO> getFormField(String formContent) {
        List<FormFieldDTO> formFields = null;
        if (Func.isNotEmpty(formContent)) {
            formFields = getFormField(JSON.parseObject(formContent));
        }
        return formFields;

    }

    @Override
    public List<FormFieldDTO> getFormField(Long formId) {
        SysForm form = sysFormMapper.selectSysFormById(formId);
        List<FormFieldDTO> formFields = null;
        if (Func.isNotEmpty(form)) {
            formFields = getFormField(form.getFormContent());
        }
        return formFields;

    }

    @Override
    public List<FormFieldDTO> getSceneFieldList(List<FormFieldDTO> fieldList, SceneGroupEnum sceneGroup) {
        if (Func.isNotEmpty(fieldList)) {
            return fieldList.stream().filter(p -> Func.isNotEmpty(p.getSceneGroup()) && p.getSceneGroup().contains(sceneGroup.name())).collect(Collectors.toList());
        }
        return null;
    }

    @Override
    public List<FormFieldDTO> getSceneFieldList(SysForm form, SceneGroupEnum sceneGroup) {
        List<FormFieldDTO> allField = getFormField(form.getFormContent());
        return getSceneFieldList(allField, sceneGroup);
    }


    @Override
    public List<FormFieldDTO> getQueryFieldList(List<FormFieldDTO> allField) {
        if (Func.isNotEmpty(allField)) {
            return allField.stream().filter(p -> Func.isNotEmpty(p.getQueryType())).collect(Collectors.toList());
        }
        return null;
    }

    @Override
    public List<FormFieldDTO> getQueryFieldList(SysForm form) {
        List<FormFieldDTO> allField = getFormField(form.getFormContent());
        return getQueryFieldList(allField);
    }

    @Override
    public SysForm selectSysFormByDeployId(Long deployId) {
        SysDeployForm deployForm=deployFormService.selectSysDeployFormByDeployId(deployId.toString());
        if (Func.isNotEmpty(deployForm)) {
            return selectSysFormById(deployForm.getFormId());
        }
        return null;
    }
}
